/* Copyright (C) 2007-2008  klevgert  http://code.google.com/p/db4o-netbeans/

This file is part of the db4o-netbeans module suite.

db4o-netbeans is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation and as clarified by db4objects' GPL 
interpretation policy, available at
http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
Suite 350, San Mateo, CA 94403, USA.

db4o-netbeans is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. */

package com.db4o.nb.editor.tableview;

import com.db4o.nb.editor.nodes.ClassNode;
import com.db4o.nb.editor.nodes.FieldNode;
import com.db4o.nb.editor.nodes.MoreObjectsNode;
import com.db4o.nb.editor.nodes.ObjectNode;
import com.db4o.nb.editor.nodes.ObjectSetNode;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import org.openide.ErrorManager;
import org.openide.explorer.ExplorerManager;
import org.openide.loaders.DataObject;
import org.openide.nodes.Node;
import org.openide.nodes.Node.Property;
import org.openide.nodes.Node.PropertySet;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;

/**
 * Top component which displays something.
 */
public class Db4oEditorTableViewTopComponent extends TopComponent implements TableModelListener, ExplorerManager.Provider, LookupListener {
  
  private static Db4oEditorTableViewTopComponent instance;
  
  /** path to the icon used by the component and its open action */
  static final String ICON_PATH = "com/db4o/nb/editor/resources/table.gif";
  
  private static final String PREFERRED_ID = "Db4oEditorTableViewTopComponent";

  private final ExplorerManager mgr = new ExplorerManager();
  private Lookup.Result result = null;

  private Node selectedNode = null; 
      
  private Db4oEditorTableViewTopComponent() {
    initComponents();
    setName(NbBundle.getMessage(Db4oEditorTableViewTopComponent.class, "CTL_Db4oEditorTableViewTopComponent"));
    setToolTipText(NbBundle.getMessage(Db4oEditorTableViewTopComponent.class, "HINT_Db4oEditorTableViewTopComponent"));
    setIcon(Utilities.loadImage(ICON_PATH, true));
    
  }

  public ExplorerManager getExplorerManager() {
    return mgr;
  }
  
  /** This method is called from within the constructor to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the Form Editor.
   */
  // <editor-fold defaultstate="collapsed" desc=" Erzeugter Quelltext ">//GEN-BEGIN:initComponents
  private void initComponents() {
    jLabel1 = new javax.swing.JLabel();
    jScrollPane1 = new javax.swing.JScrollPane();
    jTable1 = new javax.swing.JTable();

    org.openide.awt.Mnemonics.setLocalizedText(jLabel1, "[no selection]");

    jTable1.setModel(new javax.swing.table.DefaultTableModel(
      new Object [][] {

      },
      new String [] {
        "oid"
      }
    ));
    jScrollPane1.setViewportView(jTable1);

    org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
    this.setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
      .add(jLabel1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 400, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
      .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
      .add(layout.createSequentialGroup()
        .add(jLabel1)
        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
        .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 286, Short.MAX_VALUE))
    );
  }// </editor-fold>//GEN-END:initComponents
    
    
  // Variablendeklaration - nicht modifizieren//GEN-BEGIN:variables
  private javax.swing.JLabel jLabel1;
  private javax.swing.JScrollPane jScrollPane1;
  private javax.swing.JTable jTable1;
  // Ende der Variablendeklaration//GEN-END:variables
    
    /**
     * Gets default instance. Do not use directly: reserved for *.settings files only,
     * i.e. deserialization routines; otherwise you could get a non-deserialized instance.
     * To obtain the singleton instance, use {@link findInstance}.
     */
    public static synchronized Db4oEditorTableViewTopComponent getDefault() {
      if (instance == null) {
        instance = new Db4oEditorTableViewTopComponent();
      }
      return instance;
    }
    
    /**
   * Obtain the Db4oEditorTableViewTopComponent instance. Never call {@link #getDefault} directly!
   */
    public static synchronized Db4oEditorTableViewTopComponent findInstance() {
      TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
      if (win == null) {
        ErrorManager.getDefault().log(ErrorManager.WARNING, "Cannot find Db4oDataEditorView component. It will not be located properly in the window system.");
        return getDefault();
      }
      if (win instanceof Db4oEditorTableViewTopComponent) {
        return (Db4oEditorTableViewTopComponent)win;
      }
      ErrorManager.getDefault().log(ErrorManager.WARNING, "There seem to be multiple components with the '" + PREFERRED_ID + "' ID. That is a potential source of errors and unexpected behavior.");
      return getDefault();
    }
    
    public int getPersistenceType() {
      return TopComponent.PERSISTENCE_ALWAYS;
    }
    
    public void componentOpened() {
      Lookup.Template tpl = new Lookup.Template (Node.class);
      result = Utilities.actionsGlobalContext().lookup(tpl);
      result.addLookupListener (this);
    }
    
    public void componentClosed() {
      result.removeLookupListener (this);
      result = null;
    }

 
  public void tableChanged(TableModelEvent e) {
    int row = e.getFirstRow();
    int column = e.getColumn();
    
    if( row == -1 || column == -1) {
      return;
    }
    
    TableViewModel model = (TableViewModel)e.getSource();
    String columnName = model.getColumnName(column);
    Object data = model.getValueAt(row, column);

    if( this.selectedNode != null ) {
      DataObject dataObj = (DataObject)this.selectedNode.getLookup().lookup(DataObject.class);
      if( dataObj != null ) {
        
//        provider.getDatabase().getObjectContainer();
      }
    }
    // TODO
  }
   
   public void resultChanged(LookupEvent lookupEvent) {
     
      Lookup.Result r = (Lookup.Result) lookupEvent.getSource();
      Collection c = r.allInstances();
      if (!c.isEmpty()) {
        
        this.jTable1.getModel().removeTableModelListener(this);

        TableViewModel model = new TableViewModel();
        model.addTableModelListener( this );
        
        Object o =  c.iterator().next();
        if( (o instanceof ClassNode) || 
            (o instanceof ObjectNode) ||
            (o instanceof FieldNode) ||
            (o instanceof ObjectSetNode) ) {
 
          this.selectedNode = (Node)o;
      
          jLabel1.setText(selectedNode.getDisplayName());
          processNode(model, selectedNode);
  
        } else {
          String text = NbBundle.getMessage(this.getClass(), "NoSelection");
          jLabel1.setText(text);
        }
        this.jTable1.setModel(model);
  
      } else {
        String text = NbBundle.getMessage(this.getClass(), "NoSelection");
        jLabel1.setText(text);
      }
    }

  private void processNode(final TableViewModel model, final Node selectedNode) {

    ArrayList editables = new ArrayList();
    
    Node[] nodes = selectedNode.getChildren().getNodes();
    for (int i = 0; i < nodes.length; i++) {
      Node node = nodes[i];
      
      Object[] rowArray = new Object[30];
      
      if( node instanceof MoreObjectsNode ) {
        
//        editables.add( new Boolean( false ) );
        rowArray[0] = "...";
        
      } else {

        editables = new ArrayList();
        PropertySet[] propSets = node.getPropertySets();

        for (int j = 0; j < propSets.length; j++) {

          PropertySet propSet = propSets[j];
          Property[] props = propSet.getProperties();

          for (int k = 0; k < props.length; k++) {
            Property prop = props[k];
            
            editables.add( new Boolean( prop.canWrite()) );

            if( prop.canRead() ) {
              String name = prop.getName();
              Object value = null;
              try {
                value = prop.getValue();
              } catch (IllegalAccessException ex) {
                ex.printStackTrace();
              } catch (InvocationTargetException ex) {
                ex.printStackTrace();
              }
              
              int nameColIdx = findColumnIndex(model, name);
              rowArray[nameColIdx] = ( value != null )? value : "";
              
            }
          }
        }
      }
      model.addRow(rowArray);
      
      Boolean[] editArray = new Boolean[editables.size()];
      editables.toArray( editArray );
      model.setEditables( editArray );
    }
    
  }

  private int findColumnIndex(final DefaultTableModel model, final String name) {

    int nameColIdx = model.findColumn(name);
    if(  nameColIdx == -1 ) {
      model.addColumn(name);
    }
    nameColIdx = model.findColumn(name);
    return nameColIdx;
  }
   
  /** replaces this in object stream */
  public Object writeReplace() {
    return new ResolvableHelper();
  }

  protected String preferredID() {
    return PREFERRED_ID;
  }

  final static class ResolvableHelper implements Serializable {
    private static final long serialVersionUID = 1L;
    public Object readResolve() {
      return Db4oEditorTableViewTopComponent.getDefault();
    }
  }
  
  final public class TableViewModel extends DefaultTableModel {

    Boolean[] editables = new Boolean[]{};
    
    public void setEditables(Boolean[] cols) {
      this.editables = cols;
    }
    
    public boolean isCellEditable(int row, int column) {
        return ( column < editables.length && 
                 this.editables[column].booleanValue() == true );
    }
  }
}
